package com.minimalist.common.config.request;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.minimalist.common.utils.Sm4Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 响应加密处理
 *
 * @author 11987
 */
@RestControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object>{

    private static final Logger log = LoggerFactory.getLogger(EncryptResponseBodyAdvice.class);

    private final List<String> excludeUrls = new ArrayList<>(List.of(
            "/v3/api-docs",
            "/swagger-ui",
            "/swagger-resources",
            "/druid",
            "/actuator",
            "/lindi/api"));

    @Override
    public boolean supports(@NotNull MethodParameter returnType,
                            @NotNull Class<? extends HttpMessageConverter<?>> converterType){
        return true;
    }

    @Override
    public Object beforeBodyWrite(
            Object body, @NotNull MethodParameter returnType,
            @NotNull MediaType selectedContentType,
            @NotNull Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
            @NotNull ServerHttpResponse response){
        // 记录请求路径

        String requestPath = request.getURI().getPath();
        log.info("响应处理开始 - 请求路径: {}", requestPath);

        // 排除不需要加密的URL
        if (isExcludeUrl(requestPath)){
            log.info("URL在排除列表中，跳过加密: {}", requestPath);
            return body;
        }

        // 排除Feign调用，通过请求头判断
        if (isFeignRequest(request)){
            log.info("Feign请求，跳过加密: {}", requestPath);
            return body;
        }

        // 如果是null或ResponseEntity，不加密
        if (body == null){
            log.info("响应体为null，跳过加密");
            return null;
        }

        if (body instanceof ResponseEntity){
            log.info("响应体为ResponseEntity类型，跳过加密");
            return body;
        }

        // 执行加密
        log.info("开始加密响应体，类型: {}", body.getClass().getName());
        Object result = encrypt(body);
        log.info("响应体加密完成");
        return result;
    }

    /**
     * 判断是否为Feign请求
     * Feign请求通常会带有特定的请求头
     */
    private boolean isFeignRequest(ServerHttpRequest request){
        if (request == null){
            return false;
        }

        boolean hasOpenFeignHeader = request.getHeaders()
                .containsKey("X-OpenFeign-Request");
        boolean hasFeignUserAgent = request.getHeaders()
                .getFirst("User-Agent") != null &&
                Objects.requireNonNull(request.getHeaders()
                                .getFirst("User-Agent")).toLowerCase()
                        .contains("feign");

        if (hasOpenFeignHeader || hasFeignUserAgent){
            log.info("检测到Feign请求 - OpenFeign头: {}, User-Agent: {}",
                    hasOpenFeignHeader,
                    request.getHeaders().getFirst("User-Agent"));
        }

        // 检查是否包含Feign特有的请求头
        return hasOpenFeignHeader || hasFeignUserAgent;
    }

    /**
     * 判断是否为排除的URL
     */
    private boolean isExcludeUrl(String url){
        if (url == null){
            return false;
        }

        boolean isExcluded = excludeUrls.stream().anyMatch(url::contains);
        if (isExcluded){
            log.info("URL匹配排除规则: {}", url);
        }

        return isExcluded;
    }

    /**
     * 加密处理
     */
    private String encrypt(Object body){
        try{
            // 处理特殊类型的响应体
            if (body instanceof String stringBody){
                if (stringBody.isEmpty()){
                    log.info("响应体为空字符串，返回空对象的加密结果");
                    return Sm4Util.encrypt("{}");
                }

                // 尝试解析字符串是否为有效的JSON
                try{
                    JSON.parse(stringBody);
                    log.info("响应体是有效的JSON字符串，直接加密");
                    // 打印要加密的数据
                    log.info("加密前的数据(String类型): {}", stringBody);
                    return Sm4Util.encrypt(stringBody);
                } catch (Exception e){
                    log.info("响应体不是有效的JSON字符串，将其包装为JSON");
                    String wrappedJson = "{\"data\":\"" + stringBody + "\"}";
                    // 打印要加密的数据
                    log.info("加密前的数据(包装后): {}", wrappedJson);
                    return Sm4Util.encrypt(wrappedJson);
                }
            }

            log.info("序列化响应体为JSON");
            String jsonString = JSON.toJSONString(body
                    , JSONWriter.Feature.WriteMapNullValue
                    , JSONWriter.Feature.WriteLongAsString);
            if (jsonString == null || jsonString.isEmpty()){
                log.info("JSON序列化结果为空，返回空对象");
                jsonString = "{}";
            }
            log.info("JSON序列化完成，长度: {}", jsonString.length());

            // 打印要加密的数据（对于大型响应，可以考虑只打印部分内容）
            log.info("加密前的数据: {}", jsonString);

            log.info("开始SM4加密");
            String encrypted = Sm4Util.encrypt(jsonString);
            log.info("SM4加密完成，加密后长度: {}", encrypted.length());

            return encrypted;
        } catch (Exception e){
            log.error("响应加密失败: {}", e.getMessage(), e);
            // 加密失败时，尝试返回一个有效的加密结果
            try{
                return Sm4Util.encrypt("{}");
            } catch (Exception ex){
                log.error("返回空对象加密也失败: {}", ex.getMessage(), ex);
                // 如果连空对象加密也失败，返回一个固定的加密字符串
                return "ERROR_ENCRYPTING_RESPONSE";
            }
        }
    }
}
